/*************************************************
	作者: Plane
	邮箱: 1785275942@qq.com	
	功能: 使用外部帧循环调用驱动，并在帧循环中回调

    //=================*=================\\
           教学官网：www.qiqiker.com
           官方微信服务号: qiqikertuts
           Plane老师微信: PlaneZhong
               ~~获取更多教学资讯~~
    \\=================*=================//
*************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace PETimer {
    /// <summary>
    /// 基于帧循环的定时器
    /// 注意：此定时器不是线程安全的，请确保只在单线程（如Unity主线程）中使用
    /// </summary>
    public class FrameTimer : PETimer {
        private ulong currentFrame;
        private readonly Dictionary<int, FrameTask> taskDic;
        private const string tidLock = "FrameTimer_tidLock";
        private List<int> tidLst;
        public FrameTimer(ulong frameID = 0) {
            currentFrame = frameID;
            taskDic = new Dictionary<int, FrameTask>();
            tidLst = new List<int>();
        }

        public override int AddTask(
            uint delay,
            Action<int> taskCB,
            Action<int> cancelCB,
            int count = 1) {
            int tid = GenerateTid();
            ulong destFrame = currentFrame + delay;
            FrameTask task = new FrameTask(tid, delay, count, destFrame, taskCB, cancelCB);
            
            // 注意：由于 Unity 可能使用 .NET Standard 2.0，Dictionary.TryAdd 可能不可用
            // 此方法应在单线程中使用（如 Unity 主线程），因此 ContainsKey + Add 是安全的
            if(!taskDic.ContainsKey(tid)) {
                taskDic.Add(tid, task);
                return tid;
            }
            else {
                WarnFunc?.Invoke($"key:{tid} already exist.");
                return -1;
            }
        }

        public override bool DeleteTask(int tid) {
            if(taskDic.TryGetValue(tid, out FrameTask task)) {
                if(taskDic.Remove(tid)) {
                    task.cancelCB?.Invoke(tid);
                    return true;
                }
                else {
                    ErrorFunc?.Invoke($"Remove tid:{tid} in taskDic failed.");
                    return false;
                }
            }
            else {
                WarnFunc?.Invoke($"tid:{tid} is not exist.");
                return false;
            }
        }
        public override void Reset() {
            taskDic.Clear();
            tidLst.Clear();
            currentFrame = 0;
        }

        public override bool HasTask(int tid) {
            return taskDic.ContainsKey(tid);
        }

        public override TaskInfo? GetTaskInfo(int tid) {
            if(taskDic.TryGetValue(tid, out FrameTask task)) {
                return new TaskInfo {
                    tid = task.tid,
                    delay = task.delay,
                    remainingCount = task.count > 0 ? task.count : -1,
                    status = TaskStatus.Running
                };
            }
            return null;
        }

        public override List<int> GetAllTaskIds() {
            return new List<int>(taskDic.Keys);
        }

        public override int GetTaskCount() {
            return taskDic.Count;
        }
        public void UpdateTask() {
            ++currentFrame;
            tidLst.Clear();

            foreach(var item in taskDic) {
                FrameTask task = item.Value;
                if(task.destFrame <= currentFrame) {
                    task.taskCB.Invoke(task.tid);
                    task.destFrame += task.delay;
                    --task.count;
                    if(task.count == 0) {
                        tidLst.Add(task.tid);
                    }
                }
            }

            for(int i = 0; i < tidLst.Count; i++) {
                if(taskDic.Remove(tidLst[i])) {
                    LogFunc?.Invoke($"Task tid:{tidLst[i]} run to completion.");
                }
                else {
                    ErrorFunc?.Invoke($"Remove tid:{tidLst[i]} task in taskDic failed.");
                }
            }
        }

        protected override int GenerateTid() {
            // 使用原子操作优化性能
            int newTid = Interlocked.Increment(ref tid);
            if(newTid == int.MaxValue || newTid < 0) {
                lock(tidLock) {
                    if(tid >= int.MaxValue || tid < 0) {
                        tid = 0;
                    }
                    newTid = Interlocked.Increment(ref tid);
                }
            }

            // 如果ID已存在，使用传统方法查找
            if(taskDic.ContainsKey(newTid)) {
                lock(tidLock) {
                    int startTid = newTid;
                    do {
                        newTid = Interlocked.Increment(ref tid);
                        if(newTid == int.MaxValue || newTid < 0) {
                            tid = 0;
                            newTid = Interlocked.Increment(ref tid);
                        }
                        if(!taskDic.ContainsKey(newTid)) {
                            return newTid;
                        }
                    } while(newTid != startTid);
                }
            }

            return newTid;
        }

        class FrameTask {
            public int tid;
            public uint delay;
            public int count;
            public ulong destFrame;
            public Action<int> taskCB;
            public Action<int> cancelCB;
            public FrameTask(
                int tid,
                uint delay,
                int count,
                ulong destFrame,
                Action<int> taskCB,
                Action<int> cancelCB) {
                this.tid = tid;
                this.delay = delay;
                this.count = count;
                this.destFrame = destFrame;
                this.taskCB = taskCB;
                this.cancelCB = cancelCB;
            }
        }
    }
}


